﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace IPCByMessages
{
    public abstract class SyncMessagesChannelBase : MessagesChannelBase
    {
        private object locker = new object();

        /// <summary>
        /// Thread used to receive data from socket
        /// </summary>
        private Thread receiveThread = null;

        /// <summary>
        /// Close connection and release all resources.
        /// Channel becomes not usable after call.
        /// </summary>
        public override void Close()
        {
            Close(false);
        }

        /// <summary>
        /// Send message
        /// </summary>
        /// <param name="data">Data to send</param>
        public override void SendMessage(params byte[] data)
        {
            if (!Active || (otherSide == null) || !otherSide.Connected)
                return;

            var toSend = parser.MakePacket(data);

            try
            {
                otherSide.Send(toSend);

                //otherSide.BeginSend(toSend,
                //    0,
                //    toSend.Length,
                //    SocketFlags.None,
                //    null,
                //    null);
            }
            catch (SocketException)
            {
                Close(true);
            }
        }

        #region Protected methods

        /// <summary>
        /// Begin to wait for message. Receive buffer is passed as State
        /// </summary>
        protected virtual void BeginReceive()
        {
            if ((otherSide == null) || !otherSide.Connected)
            {
                Close(true);
                return;
            }

            if (receiveThread != null)
                return;

            receiveThread = new Thread(ReceiveLoop);
            receiveThread.Start();
        }

        /// <summary>
        /// Infinitely receives messages in the loop
        /// </summary>
        protected void ReceiveLoop()
        {
            SocketError error;
            while (true)
            {
                if (Closed)
                    return;

                error = SocketError.Success;

                try
                {
                    var buffer = new byte[otherSide.Available];

                    var received = otherSide.Receive(buffer,
                        0,
                        buffer.Length,
                        SocketFlags.None,
                        out error);

                    for (int i = 0; i < buffer.Length; i++)
                    {
                        var msg = parser.HandleNewByte(buffer[i]);
                        if (msg != null)
                            ThrowMessageReceived(msg);
                    }
                }
                catch (SocketException)
                {
                    error = SocketError.Interrupted;
                }

                if (error != SocketError.Success)
                {
                    Close(true);
                    return;
                }

                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// Close connection and release all resources.
        /// Channel becomes not usable after call.
        /// </summary>
        /// <param name="throwDisconnected">
        /// Specifies if Disconnected event must be thrown.
        /// </param>
        protected void Close(bool throwDisconnected)
        {
            if (Closed)
                return;

            Active = false;
            Closed = true;

            if ((connection != null) && connection.Connected)
            {
                //connection.Disconnect(false);
                connection.Close();
                connection = null;
            }

            if (otherSide != null)
            {
                otherSide.Close();
                otherSide = null;
            }

            if (throwDisconnected)
                ThrowDisconnected();

            if (receiveThread != null)
            {
                receiveThread.Abort();
                receiveThread = null;
            }
        }

        #endregion
    }
}
